package parsage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import modele.Balade;
import modele.Noeud;
import modele.Terme;

import stopList.StopList;

/**
 * Classe contenant les methodes permettant de lemmatiser les mots d'un
 * paragraphe
 */
public class Lemmatiseur {

	public static String lemmatiserMot(String mot) {
		// String c = "çßãõñáéíóèàùâêîôûäëïöüÿ";
		String motPetit = mot.toLowerCase();

		// On enleve le "s" final des mots
		if (motPetit.endsWith("s")) {
			motPetit = motPetit.substring(0, motPetit.length() - 1);
		}

		// et on garde les sept 1ers caracteres

		if (motPetit.length() > 7) {
			motPetit = motPetit.substring(0, 7);
		}
		motPetit = motPetit.replace('ç', 'c');
		motPetit = motPetit.replace('ã', 'a');
		motPetit = motPetit.replace('õ', 'o');
		motPetit = motPetit.replace('ñ', 'n');
		motPetit = motPetit.replace('á', 'a');
		motPetit = motPetit.replace('é', 'e');
		motPetit = motPetit.replace('í', 'i');
		motPetit = motPetit.replace('ó', 'o');
		motPetit = motPetit.replace('è', 'e');
		motPetit = motPetit.replace('à', 'a');
		motPetit = motPetit.replace('ù', 'u');
		motPetit = motPetit.replace('â', 'a');
		motPetit = motPetit.replace('ê', 'e');
		motPetit = motPetit.replace('î', 'i');
		motPetit = motPetit.replace('ô', 'o');
		motPetit = motPetit.replace('û', 'u');
		motPetit = motPetit.replace('ä', 'a');
		motPetit = motPetit.replace('ë', 'e');
		motPetit = motPetit.replace('ï', 'i');
		motPetit = motPetit.replace('ö', 'o');
		motPetit = motPetit.replace('ü', 'u');
		motPetit = motPetit.replace('ÿ', 'y');

		return motPetit;
	}

	/**
	 * Fonction decoupant une chaine en mots
	 * 
	 * @param chaine
	 *            la chaine a decouper
	 * @param islabel
	 *            si un mot est un label contenant des majuscules (aPourDistance
	 *            peut se decouper en 3 mots), on peut choisir de les scinder.
	 * @return la liste des mots
	 */
	public static ArrayList<String> decoupeChaine(String chaine, boolean islabel) {

		ArrayList<String> listeTermes = new ArrayList<String>();
		String[] listeMots;

		// si c'est un label on separe aussi les mots
		if (islabel) {
			chaine = chaine.replaceAll(Constantes.SEPARATEURS_DE_LABELS, " $1");
		}

		listeMots = chaine.split(Constantes.SEPARATEURS);

		// La stop liste
		StopList stopListe = new StopList("ressources/stopliste.txt");
		// Pour chaque mot :
		for (int posMot = 0; posMot < listeMots.length; posMot++) {
			String mot = listeMots[posMot].toLowerCase();
			// lemmatisation
			if (!stopListe.getStopListe().contains(mot) && !mot.equals("")) {
				mot = lemmatiserMot(mot);
				if (!listeTermes.contains(mot)) {
					listeTermes.add(mot);
				}
			}
		}

		return listeTermes;
	}

	/**
	 * Methode privee decomposant un paragraphe en termes, puis creant un noeud
	 * 
	 * @param par
	 *            le paragraphe, sous forme de chaine de caracteres
	 * @param idDocument
	 *            l'id du document contenant ce paragraphe
	 * @param listeTermes
	 *            la liste de termes deja existante
	 */
	public static Noeud decomposeParagraphe(String par,
			List<Terme> listeTermes, int idDocument, String xPath) {

		// Premiere chose : on cree le noeud
		Noeud noeud = new Noeud(idDocument, xPath);

		// Il faut une liste temporaire des termes du paragraphe pour calculer
		// les frequences. Tant qu'a faire, on va compter les occurrences, donc
		// on en fait une map
		Map<Terme, Integer> mapTermesPar = new HashMap<Terme, Integer>();

		// La stopliste, pour filtrer les mots
		StopList stopListe = new StopList("ressources/stopliste.txt");

		// On decoupe le paragraphe selon les caracteres "whitespace" (espace,
		// entree, tabulation) et la ponctuation, en quantite non nulle. Ainsi,
		// la chaine "... " est un separateur constitue de 4 caracteres.
		// ATTENTION : le caractere qui ressemble a un espace apres le ? n'est
		// pas un espace, c'est un caractere blanc qui suit certains guillemets
		// !
		String[] listeMots = par.split(Constantes.SEPARATEURS);

		// La longueur du paragraphe risque de diminuer, si on croise des mots
		// de la stopliste. On la recupere avant de parcourir les mots.
		// ATTENTION a ne pas boucler en utilisant cette variable donc, puisque
		// elle va decrementer !
		int nbMots = listeMots.length;
		// Pour chaque mot :
		for (int posMot = 0; posMot < listeMots.length; posMot++) {
			String mot = listeMots[posMot];

			// On verifie que ce mot n'est pas dans la stopliste, MAIS SURTOUT
			// qu'il n'est pas vide ! En effet, si on decompose la chaine
			// "Je programme.", on obtient les mots "Je", "programme", et ""
			// juste apres le point...
			if (!stopListe.getStopListe().contains(mot) && !mot.equals("")) {

				// On lemmatise
				mot = lemmatiserMot(mot);

				// On cherche le terme correspondant
				Terme terme = chercherTerme(mot, listeTermes);
				// Si on ne trouve aucun terme, on en cree un
				if (terme == null) {
					terme = new Terme(mot);
					// On l'ajoute a la liste de termes. Ainsi, si on retrouve
					// le meme mot dans ce paragraphe, on ne creera pas de
					// deuxieme terme pour un meme mot
					listeTermes.add(terme);
				}
				// Si le terme n'a pas encore ete croise dans le paragraphe
				if (!mapTermesPar.containsKey(terme)) {
					// On lui precise qu'il est dans ce noeud, et on le memorise
					// pour le calcul de frequences
					terme.addNoeud(noeud.getIdNoeud());
					mapTermesPar.put(terme, 0);
				}
				// On indique a quelle position on l'a trouve dans le paragraphe
				// potMot va de 0 a N-1, donc on veut posMot+1 pour la position.
				terme.addPosition(posMot + 1);
				mapTermesPar.put(terme, mapTermesPar.get(terme) + 1);
			} else {
				// On a croise un mot de la stopliste, il ne faut pas qu'il
				// compte dans le calcul de la longueur du paragraphe (et le
				// calcul des frequences)
				nbMots--;
			}
		}

		// Maintenant on peut donner la longueur du paragraphe
		noeud.setLongueur(nbMots);

		// On a parcouru tous les mots du paragraphe et trouve ou cree des
		// termes. Maintenant, on calcule leur frequence dans ce noeud.
		for (Terme terme : mapTermesPar.keySet()) {
			terme.addFrequence(mapTermesPar.get(terme));
		}

		return noeud;
	}

	/**
	 * Methode privee de recherche de terme dans une liste de termes
	 * 
	 * @param mot
	 *            le mot dont on cherche le terme
	 * @param listeTermes
	 *            la liste de termes
	 * @return le terme s'il est trouve, null sinon
	 */
	private static Terme chercherTerme(String mot, List<Terme> listeTermes) {
		Terme termeCherche = null;
		for (Terme terme : listeTermes) {
			if (terme.getTerme().equals(mot)) {
				termeCherche = terme;
			}
		}
		return termeCherche;
	}

	public static void calculScore(Terme terme, Balade balade) {

		// Calcul de l'idf
		String listeFreq = terme.getFrequences();
		String[] listeFreqParNoeud = listeFreq.split("n");
		// On prend -1 car on ne compte pas la chaine est de la forme
		// "n1f3n5f1..."
		// donc le split va donner ["", "1f3", "5f1", ...]
		int nbNoeudsAvecTerme = listeFreqParNoeud.length - 1;
		double idf = calculIdf(balade.getNoeuds().size(), nbNoeudsAvecTerme);
		terme.setIdf("" + idf);

		for (String freqParNoeud : listeFreqParNoeud) {
			if (freqParNoeud.equals("")) {
				continue;
			}
			String[] noeudEtFreq = freqParNoeud.split("f");
			int idNoeud = Integer.parseInt(noeudEtFreq[0]);
			int freq = Integer.parseInt(noeudEtFreq[1]);
			// Rappel : les id des noeuds commencent a 1
			Noeud noeud = balade.getNoeuds().get(idNoeud - 1);
			double tf = calculTf(freq, noeud.getLongueur(),
					balade.getLongueurMoyNoeud());
			terme.setTf(terme.getTf() + "n" + idNoeud + "t" + tf);

			// Ponderation utilisant le contenu des titres et sous titres
			// Si un terme dans un noeud est contenu dans son titre ou sous
			// titre, il faut augmenter le score de ce noeud par rapport a ce
			// terme. On a stocke la liste des mots lemmatises par titre dans la
			// balade, on va les comparer au terme courant

			// Le multiplicateur, initialise a 1
			int facteurTitres = 1;
			// Le mot du terme, le xpath du noeud
			String mot = terme.getTerme();
			String xPath = noeud.getxPath();

			// Boucle sur les titres de la balade
			for (String xPathTitre : balade.getMapTitres().keySet()) {
				// En realite, dans la map on stocke le xpath du noeud parent du
				// titre. Ici on teste si le xpath du noeud courant contient le
				// xpath du noeud parent du titre ; cela revient a tester si le
				// titre est un "frere" ou un "oncle" du paragraphe contenant le
				// terme
				if (xPath.contains(xPathTitre)) {
					// Si c'est le cas, et qu'en plus le terme est contenu dans
					// le titre :
					if (balade.getMapTitres().get(xPathTitre).contains(mot)) {
						// On augmente le multiplicateur
						// System.out.println("Le terme " + mot
						// + " du paragraphe " + xPath
						// + " est contenu dans son titre " + xPathTitre);
						facteurTitres = facteurTitres * 2;
					}
				}
			}

			// Boucle sur les sous titres de la balade
			for (String xPathSsTitre : balade.getMapSsTitres().keySet()) {
				// En realite, dans la map on stocke le xpath du noeud parent du
				// sous titre. Ici on teste si le xpath du noeud courant
				// contient le xpath du noeud parent du sous titre ; cela
				// revient a tester si le sous titre est un "frere" ou un
				// "oncle" du paragraphe contenant le terme
				if (xPath.contains(xPathSsTitre)) {
					// Si c'est le cas, et qu'en plus le terme est contenu dans
					// le sous titre :
					if (balade.getMapSsTitres().get(xPathSsTitre).contains(mot)) {
						// On augmente le multiplicateur
						// System.out.println("Le terme " + mot
						// + " du paragraphe " + xPath
						// + " est contenu dans son sous titre "
						// + xPathSsTitre);
						facteurTitres = facteurTitres * 2;
					}
				}
			}

			// Le multiplicateur entre en jeu dans le calcul du score. Si le
			// terme n'a pas ete rencontre dans un de ses titres ou sous titres,
			// le multiplicateur reste a 1
			terme.setScore(terme.getScore() + "n" + idNoeud + "s"
					+ (idf * tf * facteurTitres));
		}
	}

	/**
	 * Methode pour calculer l'IDF Remarques : pour nbNoeud : est-ce que tu peux
	 * faire un compteur tout bete ? pour nbNoeudsAvecTerme : peut-etre que tu
	 * peux stocker un compteur dans la classe Terme ?
	 * 
	 * @param nbNoeuds
	 * @param nbNoeudsAvecTerme
	 * @return
	 */
	private static double calculIdf(int nbNoeuds, int nbNoeudsAvecTerme) {
		return Math.log((double) nbNoeuds / (double) nbNoeudsAvecTerme);
	}

	/**
	 * Methode pour calculer le TF Remarques : la frequence aussi ca peut etre
	 * un compteur, non ? (puisque tu l'ajoute avec la methode addFrequence ...
	 * 
	 * @param frequence
	 * @param longueurNoeud
	 * @param longueurNoeudMoy
	 * @return
	 */
	private static double calculTf(int frequence, int longueurNoeud,
			int longueurNoeudMoy) {
		double tf = 0;
		double freq = (double) frequence;
		double lonN = (double) longueurNoeud;
		double lonNMoy = (double) longueurNoeudMoy;

		tf = freq / (freq + 0.5 + 1.5 * (lonN / lonNMoy));
		return tf;
	}
}
